package com.bbs.service.impl;

import cn.hutool.core.util.BooleanUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.Query;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
import com.baomidou.mybatisplus.core.injector.methods.Delete;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bbs.dto.Result;
import com.bbs.entity.Like;
import com.bbs.entity.Post;
import com.bbs.mapper.LikeMapper;
import com.bbs.mapper.PostMapper;
import com.bbs.service.LikeService;
import com.bbs.dto.LikeDTO;
import com.bbs.service.PostService;
import com.bbs.vo.LikeVO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.bbs.util.constant.*;

@Service
@Slf4j
public class LikeServiceImpl extends ServiceImpl<LikeMapper, Like> implements LikeService {
@Autowired
private PostMapper postMapper;
    @Resource
    RedissonClient redissonClient;
@Autowired
private PostService postService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    LikeMapper likeMapper;


    @Transactional
    @Override
    public Result createLike2(LikeDTO likeDTO) {
        //TODO可以先查询Redis缓存内是否存在该条数据，是的话说明点赞过了
        Long postId = likeDTO.getPostId();
        Long userId = likeDTO.getUserId();
//根据默认标识+用户id+帖子id获取锁,防止重复点赞
        RLock lock = redissonClient.getLock(CREATE_LIKE_LOCK + postId + ":" + userId);
        int count=1;
        while(count<MAX_RUN_COUNT)
        {
            try{
                boolean flag = lock.tryLock(TRY_WAIT_MAX_TIME, LIKE_LOCK_TIME, TimeUnit.MILLISECONDS);
                if(flag){
                    //获取锁成功
                   //
                    boolean exists = likeMapper.exists(
                            new QueryWrapper<Like>()
                            .eq("post_id", postId)
                            .eq("user_id", userId));
                if(exists) {
                    //TODO添加缓存，防止重复往数据库查询
                    return Result.fail("已经点过赞，不能重复点赞");
                }
                    //执行点赞功能(可以异步处理点赞)
                    Like like = new Like();
                    BeanUtils.copyProperties(likeDTO, like);
                    save(like);
                    //TODO 设置缓存
                    return Result.ok();
                }
                //获取锁失败重试
                else{
                    //休眠
                    Thread.sleep(100);
                    count++;
                }
            }catch (InterruptedException e){
                Thread.currentThread().interrupt();
                throw new RuntimeException("点赞操作被中断");
            }
            finally {
                    //解锁
                if(lock!=null&& lock.isLocked()&&lock.isHeldByCurrentThread())lock.unlock();
                //TODO 其实这里还有改进空间，比锁，关锁不是原子性的,可以用lua实现
            }
        }
        return Result.fail("点赞失败，请重试");
    }

    //创建点赞2//对并发性要求不高
    @Override
    public Result createLike1(LikeDTO likeDTO){
        //TODO
        //set集合:帖子id作为key,
        Long userId = likeDTO.getUserId();
        Long postId = likeDTO.getPostId();//获取帖子id
        String key = LIKE_CACHE_KEY + postId;
        //判断set集合是否有userId即
        //判断当前用户是否已经点赞(redis)
        Boolean member = stringRedisTemplate.opsForSet().isMember(key, userId.toString());
        //如果没有点赞,数据库点赞数+1
if(BooleanUtil.isFalse(member)){
    log.info("点赞操作");
    //数据库点赞数加1
    Like like = new Like();
    BeanUtils.copyProperties(likeDTO, like);
    //往点赞表插入
    save(like);
    //帖子数据加1
    boolean update = postService.update().setSql("like_num=like_num+1").eq("id", postId).update();
    //添加缓存
    if(update){
        stringRedisTemplate.opsForSet().add(key,userId.toString());
    }
}
else{
    log.info("取消点赞操作~");
    //说明已经点赞

    //取消点赞

    //数据库点赞数-1
    boolean update = postService.update().setSql("like_num=like_num-1").eq("id", postId).update();
    //删除点赞
    QueryWrapper<Like> wrapper=new QueryWrapper<>();
    wrapper.eq("user_id",userId).eq("post_id",postId);
likeMapper.delete(wrapper);
    //将用户从帖子的点赞set集合移除
    if(update){
        stringRedisTemplate.opsForSet().remove(key,userId.toString());
    }
}
return Result.ok();
    }
//采用Zset存储，便于获取前n个点赞的用户
    @Override
    public Result createLike(LikeDTO likeDTO){
        //TODO
        //set集合:帖子id作为key,
        Long userId = likeDTO.getUserId();
        Long postId = likeDTO.getPostId();//获取帖子id
        String key = LIKE_CACHE_KEY + postId;
        //判断set集合是否有userId即
        //判断当前用户是否已经点赞(redis)
        Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());
        if(score==null){//没有点过赞
            log.info("点赞操作");
            //数据库点赞数加1
            Like like = new Like();
            BeanUtils.copyProperties(likeDTO, like);
            //往点赞表插入
            save(like);
            boolean update = postService.update().setSql("like_num=like_num+1").eq("id", postId).update();
            //添加缓存
            if(update){
             //以时间戳作为权重
                stringRedisTemplate.opsForZSet().add(key,userId.toString(),System.currentTimeMillis());
            }
        }

        else{
           log.info("取消点赞");
            //说明已经点赞
            //取消点赞
            //数据库点赞数-1
            boolean update = postService.update().setSql("like_num=like_num-1").eq("id", postId).update();
            //删除点赞
            QueryWrapper<Like> wrapper=new QueryWrapper<>();
            wrapper.eq("user_id",userId).eq("post_id",postId);
            likeMapper.delete(wrapper);
            //将用户从帖子的点赞zSet集合移除
            if(update){
                stringRedisTemplate.opsForZSet().remove(key,userId.toString());
            }
        }
        return Result.ok();
    }



    @Override
    public LikeVO getLikeById(Long id) {
        Like like = getById(id);
        return convertToVO(like);
    }
    
    @Override
    public List<LikeVO> getLikesByPostId(Long postId) {
        LambdaQueryWrapper<Like> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Like::getPostId, postId);
        List<Like> likes = list(wrapper);
        return likes.stream()
                .map(this::convertToVO)
                .collect(Collectors.toList());
    }
    
    @Override
    public void deleteLike(Long id) {
        removeById(id);
    }
    
    @Override
    public List<LikeVO> getLikesByUserId(Long userId) {
        LambdaQueryWrapper<Like> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Like::getUserId, userId);
        List<Like> likes = list(wrapper);
        return likes.stream()
                .map(this::convertToVO)
                .collect(Collectors.toList());
    }
    
    @Override
    public Long getLikeCountByPostId(Long postId) {
        LambdaQueryWrapper<Like> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Like::getPostId, postId);
        return count(wrapper);
    }
    
    @Override
    public List<LikeVO> getAllLikes() {
        List<Like> likes = this.list();
        return likes.stream()
                .map(this::convertToVO)
                .collect(Collectors.toList());
    }
    
    private LikeVO convertToVO(Like like) {
        if (like == null) {
            return null;
        }
        LikeVO vo = new LikeVO();
        BeanUtils.copyProperties(like, vo);
        return vo;
    }
} 